#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "SocketClient.h"

SocketClient::SocketClient()
{
	m_fd = 0;
	m_errno = 0;
#ifdef WIN32
	WSADATA wsaData;
	int nRet = WSAStartup( 0x0202, &wsaData );
	if ( nRet != 0 ) 
	{
		SetLastError();
		printf("socket init(WSAStartup) error:%d \r\n",m_errno);
	}
#endif
}

SocketClient::~SocketClient()
{
	Close();
#ifdef WIN32
	WSACleanup();
#endif
}

int SocketClient::Open(const char* szIp,unsigned short wPort)
{
	Close();
	struct sockaddr_in addr_srv;
	int nRet = 0;

#ifdef WIN32
	if (m_errno!=0)
	{
		printf("socket init(WSAStartup) error:%d \r\n",m_errno);
		return m_errno;
	}
#endif

#ifdef WIN32
	m_fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
#else
	m_fd = socket(AF_INET,SOCK_STREAM,0);
#endif
	if (m_fd < 1) 
	{
		printf("socket create error:%d \r\n",SetLastError());
		return m_errno;
	}

	struct linger so_linger;
	so_linger.l_onoff = TRUE;
	so_linger.l_linger = 30;
	nRet = setsockopt(m_fd,SOL_SOCKET,SO_LINGER,(const char*)&so_linger,sizeof(so_linger));
	if (nRet == -1) 
	{
		SetLastError();
		Close();
		return m_errno;
	}

	addr_srv.sin_family = AF_INET;
	addr_srv.sin_addr.s_addr = inet_addr(szIp);
	addr_srv.sin_port = htons(wPort);


	nRet = connect( m_fd, (sockaddr*)&addr_srv, sizeof(addr_srv));
	if (nRet == -1) 
	{
		SetLastError();
		//printf("socket connect %s:%d error:%d \r\n",szIp,wPort,m_errno);
		Close();
		return m_errno;
	}
	

#ifdef WIN32
	unsigned int dwMillisecond = 500;
	nRet = setsockopt(m_fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&dwMillisecond, sizeof(dwMillisecond));
	if (nRet==SOCKET_ERROR)
	{
		printf("socket setsockopt(SO_RCVTIMEO) error:%d \r\n",SetLastError());
		return m_errno;
	}
	nRet = setsockopt(m_fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&dwMillisecond,sizeof(dwMillisecond));
	if (nRet==SOCKET_ERROR)
	{
		printf("socket setsockopt(SO_RCVTIMEO) error:%d \r\n",errno);
		return errno;
	}
#else
	struct timeval tv;
	tv.tv_sec = 0;
	tv.tv_usec = 500*1000;

	nRet = setsockopt(m_fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv));
	if (nRet==SOCKET_ERROR)
	{
		printf("socket setsockopt(SO_RCVTIMEO) error:%d \r\n",SetLastError());
		return m_errno;
	}

	nRet = setsockopt(m_fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(tv));
	if (nRet==SOCKET_ERROR)
	{
		printf("socket setsockopt(SO_SNDTIMEO) error:%d \r\n",SetLastError());
		return m_errno;
	}
#endif

	//int opt = 1;
	//if (setsockopt(m_sockfd, IPPROTO_TCP,TCP_NODELAY,(char*)&opt, sizeof(opt))==SOCKET_ERROR)
	//{
	//	printf("Couldn't setsockopt(TCP_NODELAY)\n");
	//	return WSAGetLastError();
	//}
	return 0;
}

int SocketClient::Send(void* buf,unsigned int dwDataLen)
{
	int n_sended;
	unsigned int dwTotalLen = dwDataLen;
	char* pBuffer = (char*)buf;
	while(dwDataLen)
	{
		n_sended = send( m_fd, pBuffer, dwDataLen,MSG_NOSIGNAL);
		if (n_sended>0)
		{
			dwDataLen -= n_sended;
			pBuffer	  += n_sended;	
		}
		else
		{
			//EPIPE Broken pipe
			//EAGAIN:Resource temporarily unavailable
			if (errno==EAGAIN)
			{
				continue;
			}
			SetLastError();
			Close();
			break;
		}
	}
	return dwTotalLen - dwDataLen;


	//int n_sended = send( m_fd_connect, buf, dwDataLen, 0 );
	//if (n_sended == SOCKET_ERROR) 
	//{
	//	m_errno = errno;
	//}
	//return n_sended;
}

int SocketClient::Recv(void* pBuffer,unsigned int dwLen,unsigned int dwTimeout)
{
	int nRecvedSize = 0;
	char* pBufPos = (char*)pBuffer;
	int nReadingSize = 0;
	fd_set set_read;
	struct timeval tv;
	unsigned int dwEndTime = GetTickCount() + dwTimeout;
	int nSelectedSize;

	while( (dwLen > 0) &&  ( GetTickCount() < dwEndTime) )
	{
		//nReadingSize = (dwLen>PACK_MTU)?PACK_MTU:dwLen;
		FD_ZERO(&set_read);
		FD_SET(m_fd,&set_read);

		tv.tv_sec = 0;
		tv.tv_usec = 500*1000;
		nSelectedSize = select(m_fd+1, &set_read, NULL, NULL, &tv);
		if( nSelectedSize == 0) 
		{
			continue;
		}
		else
		{
			if (FD_ISSET(m_fd,&set_read))
			{
				nReadingSize = recv(m_fd, pBufPos,dwLen,0);
				if (nReadingSize <1)
				{
					Close();
					return 0;
				}
				pBufPos += nReadingSize;
				dwLen -= nReadingSize;
				nRecvedSize += nReadingSize;
			}
		}
	}
	return nRecvedSize;
}
int SocketClient::GetLastError() const
{
	return m_errno;
}

bool SocketClient::IsOpened() const
{
	return (m_fd>0);
}

void SocketClient::Close()
{
	if (m_fd>0)
	{
	#ifdef WIN32
		closesocket(m_fd);
	#else
		close(m_fd);
	#endif
		m_fd = 0;
	}
}
int SocketClient::SetLastError()
{
#ifdef WIN32
	m_errno = WSAGetLastError();
#else
	m_errno = errno;
#endif
	return m_errno;
}


#ifndef WIN32 
unsigned int SocketClient::GetTickCount()
{
	struct timeval tv;
	if(gettimeofday(&tv, NULL)!= 0)
		return 0;
	return (tv.tv_sec*1000)+(tv.tv_usec/1000); 
}
#endif